﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using System;

namespace Asplode.InputHandler
{
    public enum MouseButtons
    {
        Left, Middle, Right, XButton1, XButton2
    }

    public delegate void InputActionDelegate(InputAction action, GameTime gameTime);
    public delegate void InputActionAxisDelegate(InputAction action, Vector2 point, GameTime gameTime);
    public delegate void InputActionTriggerScrollDelegate(InputAction action, float value, GameTime gameTime);

    public class InputAction
    {
        private string id;
        public string Identifier
        {
            get
            {
                return id;
            }
            set
            {
                if (string.IsNullOrEmpty(id))
                    id = value;
            }
        }

        #region Keyboard
        public bool UseKeyboardKey { get; set; }
        public Keys KeyboardKey { get; set; }
        #endregion

        public event InputActionDelegate ActionDown;
        public event InputActionDelegate ActionHeld;
        public event InputActionDelegate ActionUp;

        #region Mouse
        public bool UseMouseButton { get; set; }
        public bool UseMouseMove { get; set; }
        public bool UseMouseScroll { get; set; }
        public MouseButtons MouseButton { get; set; }
        public bool UpdateMouseAlways { get; set; }
        #endregion

        public event InputActionAxisDelegate MouseMove;
        public event InputActionTriggerScrollDelegate MouseScroll;

        #region Private Variables
        private bool keyPrevDown = false;
        private bool mousePrevDown = false;
        private int prevMouseX;
        private int prevMouseY;        
        private int prevMouseScroll;
        #endregion

        public InputAction(string identifier, bool useKey, bool useMouseButton, bool useMouseMove, bool useMouseScroll)
            : this()
        {
            Identifier = identifier;
            UseKeyboardKey = useKey;
            UseMouseButton = useMouseButton;
            UseMouseMove = useMouseMove;
            UseMouseScroll = useMouseScroll;
        }

        public InputAction()
        {            
            MouseState ms = Mouse.GetState();
            prevMouseX = ms.X;
            prevMouseY = ms.Y;
            prevMouseScroll = ms.ScrollWheelValue;            
        }

        public void UpdateKeyboard(KeyboardState state, GameTime gameTime)
        {
            if (UseKeyboardKey)
            {
                if (state.IsKeyDown(KeyboardKey))
                {
                    if (keyPrevDown)
                    {
                        // Held
                        if (ActionHeld != null)
                        {
                            ActionHeld(this, gameTime);
                        }
                    }
                    else
                    {
                        // Just pressed
                        keyPrevDown = true;

                        if (ActionDown != null)
                        {
                            ActionDown(this, gameTime);
                        }
                    }
                }
                else if (keyPrevDown)
                {
                    // Key released
                    keyPrevDown = false;

                    if (ActionUp != null)
                    {
                        ActionUp(this, gameTime);
                    }
                }
            }
        }

        public void UpdateMouse(MouseState state, GameTime gameTime)
        {
            #region MouseButton Handling
            if (UseMouseButton)
            {
                switch (MouseButton)
                {
                    case MouseButtons.Left:
                        handleMouseButtonState(state.LeftButton, gameTime);
                        break;

                    case MouseButtons.Middle:
                        handleMouseButtonState(state.MiddleButton, gameTime);
                        break;

                    case MouseButtons.Right:
                        handleMouseButtonState(state.RightButton, gameTime);
                        break;

                    case MouseButtons.XButton1:
                        handleMouseButtonState(state.XButton1, gameTime);
                        break;

                    case MouseButtons.XButton2:
                        handleMouseButtonState(state.XButton2, gameTime);
                        break;
                }
            }
            #endregion

            #region Mouse Movement Handling
            if (UseMouseMove)
            {
                int deltaX = state.X - prevMouseX;
                int deltaY = state.Y - prevMouseY;

                if ( deltaX !=0 || deltaY !=0 || UpdateMouseAlways)
                {
                    if (MouseMove != null)
                    {
                        MouseMove(this, new Vector2(state.X, state.Y), gameTime);
                    }
                }
               
                prevMouseX = state.X;
                prevMouseY = state.Y;
            }
            #endregion

            #region Mouse ScrollWheel Handling
            if (UseMouseScroll)
            {
                int delta = state.ScrollWheelValue - prevMouseScroll;

                if (delta != 0)
                {
                    if (MouseScroll != null)
                        MouseScroll(this, delta, gameTime);
                }
                prevMouseScroll = state.ScrollWheelValue;
            }
            #endregion
        }

        private void handleMouseButtonState(ButtonState state, GameTime gameTime)
        {
            if (state == ButtonState.Pressed)
            {
                if (mousePrevDown)
                {
                    // Held
                    if (ActionHeld != null)
                    {
                        ActionHeld(this, gameTime);
                    }
                }
                else
                {
                    // Just pressed
                    mousePrevDown = true;

                    if (ActionDown != null)
                    {
                        ActionDown(this, gameTime);
                    }
                }
            }
            else if (mousePrevDown)
            {
                // Just released
                mousePrevDown = false;

                if (ActionUp != null)
                {
                    ActionUp(this, gameTime);
                }
            }
        }
    }
}
